#include "DX11Renderer.h"

DX11Renderer::DX11Renderer(HINSTANCE pHInstance, int pShowWindowParam, bool pIsDebug)
	: hInstance(pHInstance), showWindowParam(pShowWindowParam), isDebug(pIsDebug) {
	dataLoader = new DataLoader();
	world = NULL;
}

DX11Renderer::~DX11Renderer() {
	delete window;
	delete dataLoader;
}

bool DX11Renderer::initialise() {
	WindowClass windowClass = WindowClass(hInstance);
	WindowDescription windowDescription = WindowDescription(
		hInstance,
		showWindowParam,
		TurnsDataModel::WINDOW_TITLE,
		TurnsDataModel::WINDOW_DEFAULT_WIDTH,
		TurnsDataModel::WINDOW_DEFAULT_HEIGHT,
		true
	);
	window = new Window(&windowClass, &windowDescription);
	window->registerClass();
	window->create();
	window->show();

	return initialiseDX11() && initialiseScene();
}

bool DX11Renderer::initialiseDX11() {
	HRESULT hResult;
	DXGI_MODE_DESC bufferDescription;
	DXGI_SWAP_CHAIN_DESC swapChainDescription;
	D3D11_TEXTURE2D_DESC depthStencilDescription;

	ZeroMemory(&bufferDescription, sizeof(bufferDescription));
	bufferDescription.Width = TurnsDataModel::WINDOW_DEFAULT_WIDTH;
	bufferDescription.Height = TurnsDataModel::WINDOW_DEFAULT_HEIGHT;
	bufferDescription.RefreshRate.Numerator = 0;					// VSync Off
	bufferDescription.RefreshRate.Denominator = 1;
	bufferDescription.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
	bufferDescription.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
	bufferDescription.Scaling = DXGI_MODE_SCALING_STRETCHED;

	ZeroMemory(&swapChainDescription, sizeof(swapChainDescription));
	swapChainDescription.BufferDesc = bufferDescription;
	swapChainDescription.SampleDesc.Count = 1;
	swapChainDescription.SampleDesc.Quality = 0;
	swapChainDescription.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
	swapChainDescription.BufferCount = 1;
	swapChainDescription.OutputWindow = window->getHandle();
	swapChainDescription.Windowed = window->getIsWindowed();
	swapChainDescription.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;

	std::vector<IDXGIAdapter*> videoCards = getVideoCards();
	if (videoCards.size() == 0) {
		return false;
	}

	UINT deviceFlags = 0;
	if (isDebug) {
		deviceFlags |= D3D11_CREATE_DEVICE_DEBUG;
	}

	D3D_FEATURE_LEVEL featureLevels[] = { D3D_FEATURE_LEVEL_11_0 };

	bool isSuccess = false;
	for (std::vector<IDXGIAdapter*>::iterator iterator = videoCards.begin(); iterator != videoCards.end(); ++iterator) {
		IDXGIAdapter *videoCard = *iterator;

		hResult = D3D11CreateDeviceAndSwapChain(
			videoCard,
			D3D_DRIVER_TYPE_UNKNOWN,
			NULL,							// using own driver, so no software dll needed
			deviceFlags,
			featureLevels,
			ARRAYSIZE(featureLevels),
			D3D11_SDK_VERSION,
			&swapChainDescription,
			&swapChain,
			&device,
			NULL,							// [out] don't need supported feature level info
			&deviceContext
			);

		if (!FAILED(hResult)) {
			isSuccess = true;
			break;
		}
	}
	if (!isSuccess) {
		WindowOperations::showDefaultErrorMessage(TurnsDataModel::DX11_NO_SUPPORTED_VIDEO_CARD_MESSAGE);
		return false;
	}

	ID3D11Texture2D *backBuffer;
	hResult = swapChain->GetBuffer(0, __uuidof(backBuffer), (void**)&backBuffer);
	if (FAILED(hResult)) {
		WindowOperations::showDefaultErrorMessage(TurnsDataModel::DX11_GET_BUFFER_ERROR_MESSAGE);
		return false;
	}

	hResult = device->CreateRenderTargetView(backBuffer, NULL, &renderTargetView);
	if (FAILED(hResult)) {
		WindowOperations::showDefaultErrorMessage(TurnsDataModel::DX11_CREATE_RENDER_TARGET_VIEW_ERROR_MESSAGE);
		return false;
	}
	backBuffer->Release();

	ZeroMemory(&depthStencilDescription, sizeof(depthStencilDescription));
	depthStencilDescription.Width = TurnsDataModel::WINDOW_DEFAULT_WIDTH;
	depthStencilDescription.Height = TurnsDataModel::WINDOW_DEFAULT_HEIGHT;
	depthStencilDescription.MipLevels = 1;
	depthStencilDescription.ArraySize = 1;
	depthStencilDescription.Format = DXGI_FORMAT_D24_UNORM_S8_UINT;
	depthStencilDescription.SampleDesc.Count = 1;
	depthStencilDescription.SampleDesc.Quality = 0;
	depthStencilDescription.Usage = D3D11_USAGE_DEFAULT;
	depthStencilDescription.BindFlags = D3D11_BIND_DEPTH_STENCIL;
	depthStencilDescription.CPUAccessFlags = 0;
	depthStencilDescription.MiscFlags = 0;

	device->CreateTexture2D(&depthStencilDescription, NULL, &depthStencilBuffer);
	device->CreateDepthStencilView(depthStencilBuffer, NULL, &depthStencilView);

	deviceContext->OMSetRenderTargets(1, &renderTargetView, depthStencilView);

	return true;
}

bool DX11Renderer::initialiseScene() {
	bool success = true;

	initWorld();
	success = initVertices();

	success = compileShaders();
	if (!success) return false;

	success = createVertexShaders();
	if (!success) return false;

	success = createPixelShaders();
	if (!success) return false;

	success = createIndexBuffers();
	if (!success) return false;

	success = createVertexBuffers();
	if (!success) return false;

	success = createInputLayouts();
	if (!success) return false;

	createAndSetViewport();

	success = createConstantBuffers();
	if (!success) return false;

	initCamera();

	success = createWireFrame();
	if (!success) return false;

	success = createShaderResources();
	if (!success) return false;

	prepareData();

	return true;
}

bool DX11Renderer::initWorld() {
	world = dataLoader->loadWorld((TCHAR*)TurnsDataModel::SAMPLE_MAP_FILE_PATH);
	return (world != NULL);
}

bool DX11Renderer::initVertices() {
	std::vector<VertexColoredData *> vertexData = dataLoader->loadColorVertices(world);
	coloredVerticesCount = vertexData.size();
	if (coloredVerticesCount == 0) {
		WindowOperations::showDefaultErrorMessage(TurnsDataModel::LOADER_MAP_STRUCTURE_ERROR_MESSAGE);
		return false;
	}
	coloredVertices = new VertexColoredData[coloredVerticesCount];
	for (int i = 0; i < coloredVerticesCount; ++i) {
		coloredVertices[i] = *vertexData[i];
	}
	return true;
}

bool DX11Renderer::compileShaders() {
	HRESULT hResult;
	ID3DBlob *shaderErrorMessages;

	hResult = D3DCompileFromFile(
		TEXT("VertexColoredShader.hlsl"),
		NULL,
		NULL,
		"vertexColoredShader",
		"vs_5_0",
		D3DCOMPILE_DEBUG,
		0,
		&vertexColoredShaderBuffer,
		&shaderErrorMessages
	);
	if (FAILED(hResult)) {
		char *errorMessage = (char*)shaderErrorMessages->GetBufferPointer();
		MessageBoxA(NULL, errorMessage, "Shader Compile Error", MB_OK);
		return false;
	}

	hResult = D3DCompileFromFile(
		TEXT("VertexTexturedShader.hlsl"),
		NULL,
		NULL,
		"vertexTexturedShader",
		"vs_5_0",
		D3DCOMPILE_DEBUG,
		0,
		&vertexTexturedShaderBuffer,
		&shaderErrorMessages
	);
	if (FAILED(hResult)) {
		char *errorMessage = (char*)shaderErrorMessages->GetBufferPointer();
		MessageBoxA(NULL, errorMessage, "Shader Compile Error", MB_OK);
		return false;
	}

	hResult = D3DCompileFromFile(
		TEXT("PixelColoredShader.hlsl"),
		NULL,
		NULL,
		"pixelColoredShader",
		"ps_5_0",
		D3DCOMPILE_DEBUG,
		0,
		&pixelColoredShaderBuffer,
		&shaderErrorMessages
	);
	if (FAILED(hResult)) {
		char *errorMessage = (char*)shaderErrorMessages->GetBufferPointer();
		MessageBoxA(NULL, errorMessage, "Shader Compile Error", MB_OK);
		return false;
	}

	hResult = D3DCompileFromFile(
		TEXT("PixelTexturedShader.hlsl"),
		NULL,
		NULL,
		"pixelTexturedShader",
		"ps_5_0",
		D3DCOMPILE_DEBUG,
		0,
		&pixelTexturedShaderBuffer,
		&shaderErrorMessages
		);
	if (FAILED(hResult)) {
		char *errorMessage = (char*)shaderErrorMessages->GetBufferPointer();
		MessageBoxA(NULL, errorMessage, "Shader Compile Error", MB_OK);
		return false;
	}
	return true;
}

bool DX11Renderer::createVertexShaders() {
	HRESULT hResult;

	hResult = device->CreateVertexShader(
		vertexColoredShaderBuffer->GetBufferPointer(),
		vertexColoredShaderBuffer->GetBufferSize(),
		NULL,
		&vertexColoredShader
	);
	if (FAILED(hResult)) {
		WindowOperations::showDefaultErrorMessage(TurnsDataModel::DX11_CREATE_VERTEX_SHADER_ERROR_MESSAGE);
		return false;
	}

	hResult = device->CreateVertexShader(
		vertexTexturedShaderBuffer->GetBufferPointer(),
		vertexTexturedShaderBuffer->GetBufferSize(),
		NULL,
		&vertexTexturedShader
	);
	if (FAILED(hResult)) {
		WindowOperations::showDefaultErrorMessage(TurnsDataModel::DX11_CREATE_VERTEX_SHADER_ERROR_MESSAGE);
		return false;
	}
	return true;
}

bool DX11Renderer::createPixelShaders() {
	HRESULT hResult;

	hResult = device->CreatePixelShader(
		pixelColoredShaderBuffer->GetBufferPointer(),
		pixelColoredShaderBuffer->GetBufferSize(),
		NULL,
		&pixelColoredShader
	);
	if (FAILED(hResult)) {
		WindowOperations::showDefaultErrorMessage(TurnsDataModel::DX11_CREATE_PIXEL_SHADER_ERROR_MESSAGE);
		return false;
	}

	hResult = device->CreatePixelShader(
		pixelTexturedShaderBuffer->GetBufferPointer(),
		pixelTexturedShaderBuffer->GetBufferSize(),
		NULL,
		&pixelTexturedShader
	);
	if (FAILED(hResult)) {
		WindowOperations::showDefaultErrorMessage(TurnsDataModel::DX11_CREATE_PIXEL_SHADER_ERROR_MESSAGE);
		return false;
	}
	return true;
}

bool DX11Renderer::createIndexBuffers() {
	HRESULT hResult;
	D3D11_BUFFER_DESC indexBufferDescription;
	D3D11_SUBRESOURCE_DATA indexSubresourceData;

	int tileCount = world->getTileCount();
	DWORD *indices = new DWORD[tileCount * 6];
	for (int i = 0; i < tileCount; ++i) {
		indices[i * 6] = 0 + i * 4;
		indices[i * 6 + 1] = 1 + i * 4;
		indices[i * 6 + 2] = 2 + i * 4;
		indices[i * 6 + 3] = 2 + i * 4;
		indices[i * 6 + 4] = 3 + i * 4;
		indices[i * 6 + 5] = 0 + i * 4;
	}

	ZeroMemory(&indexBufferDescription, sizeof(indexBufferDescription));
	indexBufferDescription.Usage = D3D11_USAGE_DEFAULT;
	indexBufferDescription.ByteWidth = sizeof(DWORD) * tileCount * 6;
	indexBufferDescription.BindFlags = D3D11_BIND_INDEX_BUFFER;
	indexBufferDescription.CPUAccessFlags = 0;
	indexBufferDescription.MiscFlags = 0;

	ZeroMemory(&indexSubresourceData, sizeof(indexSubresourceData));
	indexSubresourceData.pSysMem = indices;
	indexSubresourceData.SysMemPitch = 0;
	indexSubresourceData.SysMemSlicePitch = 0;

	hResult = device->CreateBuffer(&indexBufferDescription, &indexSubresourceData, &indexColoredBuffer);
	if (FAILED(hResult)) {
		WindowOperations::showDefaultErrorMessage(TurnsDataModel::DX11_CREATE_BUFFER_ERROR_MESSAGE);
		return false;
	}

	std::map<int, TileObject*> objects = world->getObjects();
	std::map<int, TileObject*>::iterator it;
	for (it = objects.begin(); it != objects.end(); ++it) {
		TileObject *object = it->second;
		int indexCount = object->vertexIndices.size();
		DWORD *objectIndices = new DWORD[indexCount];
		for (int i = 0; i < indexCount; ++i) {
			objectIndices[i] = object->vertexIndices[i];
		}

		ID3D11Buffer *indexTexturedBuffer;
		
		ZeroMemory(&indexBufferDescription, sizeof(indexBufferDescription));
		indexBufferDescription.Usage = D3D11_USAGE_DEFAULT;
		indexBufferDescription.ByteWidth = sizeof(DWORD) * indexCount;
		indexBufferDescription.BindFlags = D3D11_BIND_INDEX_BUFFER;
		indexBufferDescription.CPUAccessFlags = 0;
		indexBufferDescription.MiscFlags = 0;

		ZeroMemory(&indexSubresourceData, sizeof(indexSubresourceData));
		indexSubresourceData.pSysMem = objectIndices;
		indexSubresourceData.SysMemPitch = 0;
		indexSubresourceData.SysMemSlicePitch = 0;

		hResult = device->CreateBuffer(&indexBufferDescription, &indexSubresourceData, &indexTexturedBuffer);
		if (FAILED(hResult)) {
			WindowOperations::showDefaultErrorMessage(TurnsDataModel::DX11_CREATE_BUFFER_ERROR_MESSAGE);
			return false;
		}

		indexTexturedBuffers.push_back(indexTexturedBuffer);
		indexTexturedCounts.push_back(indexCount);
	}

	return true;
}

bool DX11Renderer::createVertexBuffers() {
	HRESULT hResult;
	D3D11_BUFFER_DESC vertexBufferDescription;
	D3D11_SUBRESOURCE_DATA vertexSubresourceData;

	ZeroMemory(&vertexBufferDescription, sizeof(vertexBufferDescription));
	vertexBufferDescription.Usage = D3D11_USAGE_DEFAULT;
	vertexBufferDescription.ByteWidth = sizeof(VertexColoredData) * coloredVerticesCount;
	vertexBufferDescription.BindFlags = D3D11_BIND_VERTEX_BUFFER;
	vertexBufferDescription.CPUAccessFlags = 0;
	vertexBufferDescription.MiscFlags = 0;

	ZeroMemory(&vertexSubresourceData, sizeof(vertexSubresourceData));
	vertexSubresourceData.pSysMem = coloredVertices;
	vertexSubresourceData.SysMemPitch = 0;
	vertexSubresourceData.SysMemSlicePitch = 0;

	hResult = device->CreateBuffer(&vertexBufferDescription, &vertexSubresourceData, &vertexColoredBuffer);
	if (FAILED(hResult)) {
		WindowOperations::showDefaultErrorMessage(TurnsDataModel::DX11_CREATE_BUFFER_ERROR_MESSAGE);
		return false;
	}

	std::map<int, TileObject*> objects = world->getObjects();
	std::map<int, TileObject*>::iterator it;
	for (it = objects.begin(); it != objects.end(); ++it) {
		TileObject *object = it->second;
		int vertexCount = object->vertexPositions.size();
		XMFLOAT3 *objectVertices = new XMFLOAT3[vertexCount];
		for (int i = 0; i < vertexCount; ++i) {
			objectVertices[i] = object->vertexPositions[i];
		}

		ID3D11Buffer *vertexTexturedBuffer;;

		ZeroMemory(&vertexBufferDescription, sizeof(vertexBufferDescription));
		vertexBufferDescription.Usage = D3D11_USAGE_DEFAULT;
		vertexBufferDescription.ByteWidth = sizeof(VertexTexturedData) * vertexCount;
		vertexBufferDescription.BindFlags = D3D11_BIND_VERTEX_BUFFER;
		vertexBufferDescription.CPUAccessFlags = 0;
		vertexBufferDescription.MiscFlags = 0;

		ZeroMemory(&vertexSubresourceData, sizeof(vertexSubresourceData));
		vertexSubresourceData.pSysMem = objectVertices; // POSSIBLE ERROR! should VertexTexturedData array be here instead of XMFLOAT3's?
		vertexSubresourceData.SysMemPitch = 0;
		vertexSubresourceData.SysMemSlicePitch = 0;

		hResult = device->CreateBuffer(&vertexBufferDescription, &vertexSubresourceData, &vertexTexturedBuffer);
		if (FAILED(hResult)) {
			WindowOperations::showDefaultErrorMessage(TurnsDataModel::DX11_CREATE_BUFFER_ERROR_MESSAGE);
			return false;
		}

		vertexTexturedBuffers.push_back(vertexTexturedBuffer);
	}

	return true;
}

bool DX11Renderer::createConstantBuffers() {
	HRESULT hResult;
	D3D11_BUFFER_DESC constantBufferDescription;

	ZeroMemory(&constantBufferDescription, sizeof(constantBufferDescription));
	constantBufferDescription.Usage = D3D11_USAGE_DEFAULT;
	constantBufferDescription.ByteWidth = sizeof(cbPerSceneData);
	constantBufferDescription.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
	constantBufferDescription.CPUAccessFlags = 0;
	constantBufferDescription.MiscFlags = 0;

	hResult = device->CreateBuffer(&constantBufferDescription, NULL, &constantPerSceneBuffer);
	if (FAILED(hResult)) {
		WindowOperations::showDefaultErrorMessage(TurnsDataModel::DX11_CREATE_BUFFER_ERROR_MESSAGE);
		return false;
	}

	ZeroMemory(&constantBufferDescription, sizeof(constantBufferDescription));
	constantBufferDescription.Usage = D3D11_USAGE_DEFAULT;
	constantBufferDescription.ByteWidth = sizeof(cbPerObjectData);
	constantBufferDescription.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
	constantBufferDescription.CPUAccessFlags = 0;
	constantBufferDescription.MiscFlags = 0;

	hResult = device->CreateBuffer(&constantBufferDescription, NULL, &constantPerObjectBuffer);
	if (FAILED(hResult)) {
		WindowOperations::showDefaultErrorMessage(TurnsDataModel::DX11_CREATE_BUFFER_ERROR_MESSAGE);
		return false;
	}

	return true;
}

bool DX11Renderer::createInputLayouts() {
	HRESULT hResult;

	D3D11_INPUT_ELEMENT_DESC inputElementDescriptions[] = {
			{ "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 },
			{ "COLOR", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0 }
	};
	UINT inputElements = ARRAYSIZE(inputElementDescriptions);

	hResult = device->CreateInputLayout(
		inputElementDescriptions,
		inputElements,
		vertexColoredShaderBuffer->GetBufferPointer(),
		vertexColoredShaderBuffer->GetBufferSize(),
		&vertexColoredLayout
	);
	if (FAILED(hResult)) {
		WindowOperations::showDefaultErrorMessage(TurnsDataModel::DX11_CREATE_INPUT_LAYOUT_ERROR_MESSAGE);
		return false;
	}
	
	D3D11_INPUT_ELEMENT_DESC inputElementDescriptionsWithTexture[] = {
			{ "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 },
			{ "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0 }
	};
	inputElements = ARRAYSIZE(inputElementDescriptionsWithTexture);

	hResult = device->CreateInputLayout(
		inputElementDescriptionsWithTexture,
		inputElements,
		vertexTexturedShaderBuffer->GetBufferPointer(),
		vertexTexturedShaderBuffer->GetBufferSize(),
		&vertexTexturedLayout
	);
	if (FAILED(hResult)) {
		WindowOperations::showDefaultErrorMessage(TurnsDataModel::DX11_CREATE_INPUT_LAYOUT_ERROR_MESSAGE);
		return false;
	}

	return true;
}

void DX11Renderer::createAndSetViewport() {
	D3D11_VIEWPORT viewport;
	ZeroMemory(&viewport, sizeof(viewport));
	viewport.TopLeftX = 0;
	viewport.TopLeftY = 0;
	viewport.Width = TurnsDataModel::WINDOW_DEFAULT_WIDTH;
	viewport.Height = TurnsDataModel::WINDOW_DEFAULT_HEIGHT;

	deviceContext->RSSetViewports(1, &viewport);
}

void DX11Renderer::initCamera() {
	cameraPosition = TurnsDataModel::CAMERA_POSITION;
	cameraTarget = TurnsDataModel::CAMERA_TARGET;
	cameraUp = TurnsDataModel::CAMERA_UP;

	cameraView = XMMatrixLookAtLH(cameraPosition, cameraTarget, cameraUp);

	cameraProjection = XMMatrixPerspectiveFovLH(
		0.4f * TurnsDataModel::PI,
		TurnsDataModel::WINDOW_DEFAULT_WIDTH / TurnsDataModel::WINDOW_DEFAULT_HEIGHT,
		1.0f,
		1000.0f
	);
}

bool DX11Renderer::createWireFrame() {
	HRESULT hResult;
	D3D11_RASTERIZER_DESC rasterizerDescription;

	ZeroMemory(&rasterizerDescription, sizeof(D3D11_RASTERIZER_DESC));
	rasterizerDescription.FillMode = D3D11_FILL_WIREFRAME;
	rasterizerDescription.CullMode = D3D11_CULL_NONE;
	hResult = device->CreateRasterizerState(&rasterizerDescription, &wireFrame);
	if (FAILED(hResult)) {
		WindowOperations::showDefaultErrorMessage(TurnsDataModel::DX11_CREATE_WIRE_FRAME_ERROR_MESSAGE);
		return false;
	}
	return true;
}

bool DX11Renderer::createShaderResources() {
	HRESULT hResult;

	std::map<int, TileObject*> objects = world->getObjects();
	std::map<int, TileObject*>::iterator it;
	for (it = objects.begin(); it != objects.end(); ++it) {
		ID3D11ShaderResourceView *textureView;
		TileObject *object = it->second;
		std::string fullPath = TurnsDataModel::TEXTURES_FILES_PATH_STRING + object->fileName;

		hResult = CreateWICTextureFromFile(device, deviceContext, std::wstring(fullPath.begin(), fullPath.end()).c_str(), NULL, &textureView);
		if (FAILED(hResult)) {
			WindowOperations::showDefaultErrorMessage(TurnsDataModel::DX11_CREATE_TEXTURE_ERROR_MESSAGE);
			return false;
		}

		textures.push_back(textureView);
	}

	D3D11_SAMPLER_DESC samplerDescription;
	ZeroMemory(&samplerDescription, sizeof(samplerDescription));
	samplerDescription.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR;
	samplerDescription.AddressU = D3D11_TEXTURE_ADDRESS_WRAP;
	samplerDescription.AddressV = D3D11_TEXTURE_ADDRESS_WRAP;
	samplerDescription.AddressW = D3D11_TEXTURE_ADDRESS_WRAP;
	samplerDescription.ComparisonFunc = D3D11_COMPARISON_NEVER;
	samplerDescription.MinLOD = 0;
	samplerDescription.MaxLOD = D3D11_FLOAT32_MAX;

	hResult = device->CreateSamplerState(&samplerDescription, &textureSamplerState);
	if (FAILED(hResult)) {
		WindowOperations::showDefaultErrorMessage(TurnsDataModel::DX11_CREATE_TEXTURE_ERROR_MESSAGE);
		return false;
	}

	return true;
}

void DX11Renderer::prepareData() {
	objectPositions = std::map<TileObject*, std::vector<Coord>*>();
	std::map<Coord, Tile*> board = world->getBoard();
	std::map<Coord, Tile*>::iterator boardIterator;
	for (boardIterator = board.begin(); boardIterator != board.end(); ++boardIterator) {
		Coord coord = boardIterator->first;
		Tile *tile = boardIterator->second;
		std::vector<TileObject*> tileObjects = tile->getObjects();
		std::vector<TileObject*>::iterator objectIterator;
		for (objectIterator = tileObjects.begin(); objectIterator != tileObjects.end(); ++objectIterator) {
			TileObject *object = (*objectIterator);
			if (objectPositions.find(object) == objectPositions.end()) {
				objectPositions.insert(std::pair<TileObject*, std::vector<Coord>*>(object, new std::vector<Coord>()));
			}
			objectPositions[object]->push_back(coord);
		}
	}
}

std::vector<IDXGIAdapter*> DX11Renderer::getVideoCards() {
	UINT index = 0;
	IDXGIAdapter * videoCard = NULL;
	std::vector<IDXGIAdapter*> videoCards = std::vector<IDXGIAdapter*>();
	IDXGIFactory * factory = NULL;

	HRESULT hResult = CreateDXGIFactory(__uuidof(IDXGIFactory), (void**)(&factory));
	if (FAILED(hResult)) {
		WindowOperations::showDefaultErrorMessage(TurnsDataModel::DX11_DXGI_FACTORY_CREATION_ERROR_MESSAGE);
		return videoCards;
	}

	while (factory->EnumAdapters(index, &videoCard) != DXGI_ERROR_NOT_FOUND) {
		videoCards.push_back(videoCard);
		++index;
	}
	factory->Release();
	return videoCards;
}

void DX11Renderer::releaseObjects() {
	swapChain->Release();
	device->Release();
	deviceContext->Release();
	renderTargetView->Release();
	vertexColoredBuffer->Release();
	indexColoredBuffer->Release();
	vertexColoredShader->Release();
	vertexTexturedShader->Release();
	pixelColoredShader->Release();
	pixelTexturedShader->Release();
	vertexColoredShaderBuffer->Release();
	vertexTexturedShaderBuffer->Release();
	pixelColoredShaderBuffer->Release();
	pixelTexturedShaderBuffer->Release();
	vertexColoredLayout->Release();
	vertexTexturedLayout->Release();
	depthStencilView->Release();
	depthStencilBuffer->Release();
	constantPerSceneBuffer->Release();
	constantPerObjectBuffer->Release();
	wireFrame->Release();
	textureSamplerState->Release();
	for (std::vector<ID3D11Buffer*>::iterator it = indexTexturedBuffers.begin(); it != indexTexturedBuffers.end(); ++it) (*it)->Release();
	for (std::vector<ID3D11Buffer*>::iterator it = vertexTexturedBuffers.begin(); it != vertexTexturedBuffers.end(); ++it) (*it)->Release();
	indexTexturedBuffers.clear();
	vertexTexturedBuffers.clear();
}

void DX11Renderer::updateScene() {

}

void DX11Renderer::drawScene() {
	std::map<Coord, Tile*> board = world->getBoard();
	std::map<int, TileObject*> objects = world->getObjects();

	deviceContext->RSSetState(wireFrame);

	deviceContext->ClearRenderTargetView(renderTargetView, TurnsDataModel::BACKGROUND_COLOR);
	deviceContext->ClearDepthStencilView(depthStencilView, D3D11_CLEAR_DEPTH | D3D11_CLEAR_STENCIL, 1.0f, 0);

	// drawing colored tiles
	deviceContext->VSSetShader(vertexColoredShader, NULL, NULL);
	deviceContext->PSSetShader(pixelColoredShader, NULL, NULL);
	deviceContext->IASetIndexBuffer(indexColoredBuffer, DXGI_FORMAT_R32_UINT, 0);
	UINT stride = sizeof(VertexColoredData);
	UINT offset = 0;
	deviceContext->IASetVertexBuffers(0, 1, &vertexColoredBuffer, &stride, &offset);
	deviceContext->IASetInputLayout(vertexColoredLayout);
	deviceContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
	for (std::map<Coord, Tile*>::iterator it = board.begin(); it != board.end(); ++it) {
		Coord coord = it->first;
		Tile *tile = it->second;
		Land *land = tile->getLand();
		int landIndex = land->index;

		XMMATRIX translation = XMMatrixTranslation(coord.getX() * TurnsDataModel::MAP_TILE_SIZE, 0.0f, coord.getY() * TurnsDataModel::MAP_TILE_SIZE);
		
		worldMatrix = translation;
		worldViewProjectionMatrix = worldMatrix * cameraView * cameraProjection;

		cbPerSceneData.worldViewProjection = XMMatrixTranspose(worldViewProjectionMatrix);
		deviceContext->UpdateSubresource(constantPerSceneBuffer, 0, NULL, &cbPerSceneData, 0, 0);
		deviceContext->VSSetConstantBuffers(0, 1, &constantPerSceneBuffer);

		deviceContext->DrawIndexed(6, 6 * landIndex, 0);
	}

	// drawing objects
	deviceContext->VSSetShader(vertexTexturedShader, NULL, NULL);
	deviceContext->PSSetShader(pixelTexturedShader, NULL, NULL);

	int i = 0;
	/*std::map<ID3D11Buffer*, int>::iterator indexBufferIterator = indexTexturedBuffers.begin();
	std::vector<ID3D11Buffer*>::iterator vertexBufferIterator  = vertexTexturedBuffers.begin();
	std::vector<ID3D11ShaderResourceView*>::iterator textureIterator = textures.begin();*/
	for (std::map<int, TileObject*>::iterator it = objects.begin(); it != objects.end(); ++it) {
		TileObject *object = it->second;

		deviceContext->IASetIndexBuffer(indexTexturedBuffers[i], DXGI_FORMAT_R32_UINT, 0);
		UINT stride = sizeof(VertexTexturedData);
		UINT offset = 0;
		deviceContext->IASetVertexBuffers(0, 1, &(vertexTexturedBuffers[i]), &stride, &offset);
		deviceContext->IASetInputLayout(vertexTexturedLayout);
		deviceContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);

		deviceContext->PSSetShaderResources(0, 1, &(textures[i]));
		deviceContext->PSSetSamplers(0, 1, &textureSamplerState);
		
		std::vector<Coord> positions = *(objectPositions[object]);
		std::vector<Coord>::iterator coordIterator;
		for (coordIterator = positions.begin(); coordIterator != positions.end(); ++coordIterator) {
			Coord coord = *coordIterator;

			XMMATRIX translation = XMMatrixTranslation((coord.getX() + 0.5f) * TurnsDataModel::MAP_TILE_SIZE, 0.0f, (coord.getY() + 0.5f) * TurnsDataModel::MAP_TILE_SIZE);

			worldMatrix = translation;
			worldViewProjectionMatrix = worldMatrix * cameraView * cameraProjection;

			cbPerObjectData.worldViewProjection = XMMatrixTranspose(worldViewProjectionMatrix);
			deviceContext->UpdateSubresource(constantPerObjectBuffer, 0, NULL, &cbPerObjectData, 0, 0);
			deviceContext->VSSetConstantBuffers(0, 1, &constantPerObjectBuffer);

			deviceContext->DrawIndexed(indexTexturedCounts[i], 0, 0);
		}

		/*++indexBufferIterator;
		++vertexBufferIterator;
		++textureIterator;*/
		++i;
	}

	swapChain->Present(0, 0);
}

int DX11Renderer::loop() {
	bool isRunning = true;
	MSG message;
	ZeroMemory(&message, sizeof(message));
	while (isRunning) {
		if (PeekMessage(&message, NULL, 0, 0, PM_REMOVE)) {
			if (message.message == WM_QUIT) {
				isRunning = false;
			} else {
				TranslateMessage(&message);
				DispatchMessage(&message);
			}
		} else {
			updateScene();
			drawScene();
		}
	}
	return message.wParam;
}
